package concurrent.thread.singleton;

/**
 * 双重检测锁单例模式
 * <p>
 * 在软件工程中，双重检查锁（double-checked locking[1]）是一种设计模式，通过在加锁前检查锁标志（criterion），可以减少加锁的开销。只有当标志显示需要的时候才会加锁。
 */
public class DoubleCheckLockingSingletonInstance {

    /**
     * private static DoubleCheckLockingSingletonInstance instance = null;
     * 如果是上面注释中的写法，没有加volatile，虽然该单例模式在100次 1000次 10000次都不会出错，但是其实它是不安全的
     * <p>
     * instance = new DoubleCheckLockingSingletonInstance(); 分为了三个步骤
     * 1. memory = allocate(); 分配对象内存空间
     * 2. instance(memory); 初始化对象
     * 3. instance = memory; 设置instance指向刚分配的内存地址，此时instance != null
     * <p>
     * 其中 2 和 3 没有依赖关系，在没有使用volatile的时候，可能出现指令重排。 可能步骤变为 1、3、2。此时一个线程已经将instance指向了内存地址，不为空了。
     * 另外一个线程第一次判断instance!=null为true，然后得到了一个空对象
     */
    private volatile static DoubleCheckLockingSingletonInstance instance = null;

    private DoubleCheckLockingSingletonInstance() {
        System.out.println(Thread.currentThread().getName() + "\t 构造方法");
    }

    public static DoubleCheckLockingSingletonInstance getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckLockingSingletonInstance.class) {
                if (instance == null) {
                    instance = new DoubleCheckLockingSingletonInstance();
                }
            }
        }
        return instance;
    }
}
